using System;
using System.Drawing;

namespace MetaViewAnisotropicDiffusion
{
	namespace Matrix
	{
		public class MultiValuedDoubleMatrix
		{
			public int vectorLength;
			public DoubleMatrix[] comp;
			public int XDim
			{
				get{return this.comp[0].XDim;}
			}

			public int YDim
			{
				get{return this.comp[0].YDim;}
			}

			public int VectorLength
			{
				get{return vectorLength;}
			}

			public MultiValuedDoubleMatrix(int _vectorLength)
			{
				vectorLength = _vectorLength;
				comp = new DoubleMatrix[_vectorLength];
			}

			public MultiValuedDoubleMatrix(int xDim, int yDim, int _vectorLength)
			{
				vectorLength = _vectorLength;
				comp = new DoubleMatrix[_vectorLength];
				for(int i=0;i<_vectorLength;i++)
				{
					comp[i] = new DoubleMatrix(xDim, yDim);
				}
			}

			public MultiValuedDoubleMatrix(int xDim, int yDim, int _vectorLength, double init)
			{
				vectorLength = _vectorLength;
				comp = new DoubleMatrix[_vectorLength];
				for(int i=0;i<_vectorLength;i++)
				{
					comp[i] = new DoubleMatrix(xDim, yDim,init);
				}
			}

			public MultiValuedDoubleMatrix Clone()
			{
				MultiValuedDoubleMatrix clone = new MultiValuedDoubleMatrix(this.XDim, this.YDim, this.VectorLength);
				for(int i=0;i<this.VectorLength;i++)
				{
					clone.comp[i] = this.comp[i].Clone();
				}
				return clone;
			}


			public double Max()
			{
				double max = this.comp[0].Max();
				for(int i = 0;i<this.vectorLength;i++)
				{
					double m = this.comp[i].Max();
					max = max>m ? max : m;
				}
				return max;
			}

			public double Min()
			{
				double min = this.comp[0].Min();
				for(int i = 0;i<this.vectorLength;i++)
				{
					double m = this.comp[i].Min();
					min = min<m ? min : m;
				}
				return min;
			}

			public double Average()
			{
				double average = 0.0;
				for(int i=0;i<this.XDim;i++)
				{
					for(int j=0;j<this.YDim;j++)
					{
						for(int k = 0;k<this.vectorLength;k++)
						{
							average += this.comp[k].data[i,j];
						}
					}
				}
				average /= this.vectorLength*this.XDim*this.YDim;
				return average;
			}


			public void ImageScale()
			{
				double max = this.Max();
				double min = this.Min();
				//init matrix to zero if it is a constnt matrix.
				if(max == min)
				{
					for(int i = 0;i<this.vectorLength;i++)
					{
						this.comp[i].Initialize(0.0);
					}
				}
				else
				{
					for(int i = 0;i<this.vectorLength;i++)
					{
						this.comp[i].SubtractValue(min);
						this.comp[i].DivideValue(max-min);
						this.comp[i].MultiplyValue(255.0);
					}
				}

			}

			public MultiValuedDoubleMatrix SubSample(int ratioX, int ratioY, bool average)
			{
				MultiValuedDoubleMatrix tmp = new MultiValuedDoubleMatrix(this.VectorLength);
				for(int i=0; i<this.VectorLength;i++)
				{
					tmp.comp[i] = this.comp[i].SubSample(ratioX, ratioY, average);
				}
				return tmp;
			}

			public void ReadDataFromRGB(RGBDoubleMatrix rgb)
			{
				for (int i=0; i<this.XDim; i++) 
				{
					for (int j=0; j<this.YDim; j++) 
					{
						this.comp[0].data[i,j] = rgb.R.data[i,j];
						this.comp[1].data[i,j] = rgb.G.data[i,j];
						this.comp[2].data[i,j] = rgb.B.data[i,j];
					}
				}
			}

			public static  MultiValuedDoubleMatrix ReadFromRGB(RGBDoubleMatrix rgb)
			{
				MultiValuedDoubleMatrix tmp = new MultiValuedDoubleMatrix(rgb.XDim, rgb.YDim, 3);
				for (int i=0; i<rgb.XDim; i++) 
				{
					for (int j=0; j<rgb.YDim; j++) 
					{
						tmp.comp[0].data[i,j] = rgb.R.data[i,j];
						tmp.comp[1].data[i,j] = rgb.G.data[i,j];
						tmp.comp[2].data[i,j] = rgb.B.data[i,j];
					}
				}
				return tmp;
			}


			public static  MultiValuedDoubleMatrix ReadFromDouble(DoubleMatrix mat)
			{
				MultiValuedDoubleMatrix tmp = new MultiValuedDoubleMatrix(mat.XDim, mat.YDim, 1);
				for (int i=0; i<mat.XDim; i++) 
				{
					for (int j=0; j<mat.YDim; j++) 
					{
						tmp.comp[0].data[i,j] = mat.data[i,j];
					}
				}
				return tmp;
			}


			public void UnitScale()
			{
				DoubleMatrix tmp = new DoubleMatrix(this.XDim, this.YDim);
				
				for(int i=0;i<this.VectorLength;i++)
				{
					tmp += this.comp[i]*this.comp[i];
				}
				for (int i=0; i<tmp.XDim; i++) 
				{
					for (int j=0; j<tmp.YDim; j++) 
					{
						tmp.data[i,j] = Math.Sqrt(tmp.data[i,j]);
					}
				}
				double max = tmp.Max();
				//init matrix to zero if it is a zero matrix.
				if(max == 0)
				{
					for(int i=0;i<this.VectorLength;i++)
					{
						this.comp[i].Initialize(0.0);
					}
				}
				else
				{
					for(int i=0;i<this.VectorLength;i++)
					{
						this.comp[i].DivideValue(max);
					}
				}
			}

			public double EuclideanDistance(Point p1, Point p2)
			{
				double dist = 0d;
				for(int i=0;i<this.VectorLength;i++)
				{
					double diff = this.comp[i].data[p1.X,p1.Y] - this.comp[i].data[p2.X,p2.Y];
					dist += diff*diff;
				}
				return Math.Sqrt(dist);
			}


			public static DoubleMatrix AvarageFlatten(MultiValuedDoubleMatrix m)
			{
				DoubleMatrix tmp = new DoubleMatrix(m.XDim, m.YDim, 0d);
				for(int i=0;i<m.VectorLength;i++)
				{
					tmp += m.comp[i];
				}
				tmp.DivideValue((double)m.VectorLength);
				return tmp;
			}

		}
	}
}












